////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_CONTAINERS_CALLBACK_INL__
#define __WN_CONTAINERS_CALLBACK_INL__

#ifndef __WN_CONTAINERS_CALLBACK_H__
    #error "WNCallback.inl should never be included directly. Please include WNCallback.h instead"
#endif

#include "WNCore/inc/WNTypes.h"
#include "WNCore/inc/WNAtomic.h"
#include "WNCore/inc/WNAssert.h"
#include "WNMemory/inc/WNAllocation.h"
#include "WNContainers/inc/WNCallback.h"

namespace WNContainers {
    namespace __WNInternal {

        template <typename Type, typename Return>
        class __WNCallbackInternal0 : public __WNCallbackBase0<Return> {
        public:
            __WNCallbackInternal0(Return (Type::*_function)(), Type* _this) :
                __WNCallbackBase0<Return>(),
                mThis(_this),
                mFunction(_function) {
            }

            Return Execute() const {
                WN_RELEASE_ASSERT_DESC(mThis != WN_NULL, "This pointer is null");
                WN_RELEASE_ASSERT_DESC(mFunction != WN_NULL, "Invalid function pointer");

                return(mThis->mFunction());
            }

        private:
            Type* mThis;
            Return (Type::*mFunction)();
        };

        template <typename Type, typename Return, typename Parameter>
        class __WNCallbackInternal1 : public __WNCallbackBase1<Return, Parameter> {
        public:
            __WNCallbackInternal1(Return (Type::*_function)(Parameter), Type* _this) :
                __WNCallbackBase1<Return, Parameter>(),
                mThis(_this),
                mFunction(_function) {
            }

            Return Execute(const Parameter& _parameter) const {
                WN_RELEASE_ASSERT_DESC(mThis != WN_NULL, "This pointer is null");
                WN_RELEASE_ASSERT_DESC(mFunction != WN_NULL, "Invalid function pointer");

                return(mThis->mFunction(_parameter));
            }

        private:
            Type* mThis;
            Return (Type::*mFunction)(Parameter);
        };

        template <typename Type, typename Return, typename Parameter1, typename Parameter2>
        class __WNCallbackInternal2 : public __WNCallbackBase2<Return, Parameter1, Parameter2> {
        public:
            __WNCallbackInternal2(Type* _this, Return (Type::*_function)(Parameter1, Parameter2)) :
                __WNCallbackBase2<Return, Parameter1, Parameter2>(),
                mThis(_this),
                mFunction(_function) {
            }

            Return Execute(const Parameter1& _parameter1, const Parameter2& _parameter2) const {
                WN_RELEASE_ASSERT_DESC(mThis != WN_NULL, "This pointer is null");
                WN_RELEASE_ASSERT_DESC(mFunction != WN_NULL, "Invalid function pointer");

                return(((*mThis).*mFunction)(_parameter1, _parameter2));
            }

        private:
            Type* mThis;
            Return (Type::*mFunction)(Parameter1, Parameter2);
        };

        template <typename Type, typename Return, typename Parameter1, typename Parameter2, typename Parameter3>
        class __WNCallbackInternal3 : public __WNCallbackBase3<Return, Parameter1, Parameter2, Parameter3> {
        public:
            __WNCallbackInternal3(Type* _this, Return (Type::*_function)(Parameter1, Parameter2, Parameter3)) :
                __WNCallbackBase3<Return, Parameter1, Parameter2, Parameter3>(),
                mThis(_this),
                mFunction(_function) {
            }

            Return Execute(const Parameter1& _parameter1, const Parameter2& _parameter2, const Parameter3& _parameter3) const {
                WN_RELEASE_ASSERT_DESC(mThis != WN_NULL, "This pointer is null");
                WN_RELEASE_ASSERT_DESC(mFunction != WN_NULL, "Invalid function pointer");

                return(((*mThis).*mFunction)(_parameter1, _parameter2, _parameter3));
            }

        private:
            Type* mThis;
            Return (Type::*mFunction)(Parameter1, Parameter2, Parameter3);
        };

        template <typename Return>
        class __WNCallbackClasslessInternal0 : public __WNCallbackBase0<Return> {
        public:
            __WNCallbackClasslessInternal0(Return (*_function)()) :
                __WNCallbackBase0<Return>(),
                mFunction(_function) {
            }

            virtual Return Execute() const {
                WN_RELEASE_ASSERT_DESC(mFunction != WN_NULL, "Invalid function pointer");

                return(mFunction());
            }

        private:
            Return (*mFunction)();
        };

        template <typename Return, typename Parameter>
        class __WNCallbackClasslessInternal1 : public __WNCallbackBase1<Return, Parameter> {
        public:
            __WNCallbackClasslessInternal1(Return (*_function)(Parameter)) :
                __WNCallbackBase1<Return, Parameter>(),
                mFunction(_function) {
            }

            virtual Return Execute(const Parameter& _parameter) const {
                WN_RELEASE_ASSERT_DESC(mFunction != WN_NULL, "Invalid function pointer");

                return(mFunction(_parameter));
            }

        private:
            Return (*mFunction)(Parameter);
        };

        template <typename Return, typename Parameter1, typename Parameter2>
        class __WNCallbackClasslessInternal2 : public __WNCallbackBase2<Return, Parameter1, Parameter2> {
        public:
            __WNCallbackClasslessInternal2(Return (*_function)(Parameter1, Parameter2)) :
                __WNCallbackBase2<Return, Parameter1, Parameter2>(),
                mFunction(_function) {
            }

            virtual Return Execute(const Parameter1& _parameter1, const Parameter2& _parameter2) const {
                WN_RELEASE_ASSERT_DESC(mFunction != WN_NULL, "Invalid function pointer");

                return(mFunction(_parameter1, _parameter2));
            }

        private:
            Return (*mFunction)(Parameter1, Parameter2);
        };

        template <typename Return, typename Parameter1, typename Parameter2, typename Parameter3>
        class __WNCallbackClasslessInternal3 : public __WNCallbackBase3<Return, Parameter1, Parameter2, Parameter3> {
        public:
            __WNCallbackClasslessInternal3(Return (*_function)(Parameter1, Parameter2, Parameter3)) :
                __WNCallbackBase3<Return, Parameter1, Parameter2, Parameter3>(),
                mFunction(_function) {
            }

            virtual Return Execute(const Parameter1& _parameter1, const Parameter2& _parameter2, const Parameter3& _parameter3) const {
                WN_RELEASE_ASSERT_DESC(mFunction != WN_NULL, "Invalid function pointer");

                return(mFunction(_parameter1, _parameter2, _parameter3));
            }

        private:
            Return (*mFunction)(Parameter1, Parameter2, Parameter3);
        };
        
        template<typename Return>
        __WNCallbackBase0<Return>::__WNCallbackBase0() :
            mCount(0) {
        }

        template<typename Return>
        WN_VOID __WNCallbackBase0<Return>::AddRef() {
            WNAtomicIncrement(&mCount);
        }

        template<typename Return>
        WN_ATOM_T __WNCallbackBase0<Return>::RemoveRef() {
            return(WNAtomicDecrement(&mCount));
        }
        
        template<typename Return, typename Parameter>
        __WNCallbackBase1<Return, Parameter>::__WNCallbackBase1() :
            mCount(0) {
        }

        template<typename Return, typename Parameter>
        WN_VOID __WNCallbackBase1<Return, Parameter>::AddRef() {
            WNAtomicIncrement(&mCount);
        }

        template<typename Return, typename Parameter>
        WN_ATOM_T __WNCallbackBase1<Return, Parameter>::RemoveRef() {
            return(WNAtomicDecrement(&mCount));

            return(mCount);
        }

        template<typename Return, typename Parameter1, typename Parameter2>
        __WNCallbackBase2<Return, Parameter1, Parameter2>::__WNCallbackBase2() :
            mCount(0) {
        }

        template<typename Return, typename Parameter1, typename Parameter2>
        WN_VOID __WNCallbackBase2<Return, Parameter1, Parameter2>::AddRef() {
            WNAtomicIncrement(&mCount);
        }

        template<typename Return, typename Parameter1, typename Parameter2>
        WN_ATOM_T __WNCallbackBase2<Return, Parameter1, Parameter2>::RemoveRef() {
            return(WNAtomicDecrement(&mCount));
        }

        template<typename Return, typename Parameter1, typename Parameter2, typename Parameter3>
        __WNCallbackBase3<Return, Parameter1, Parameter2, Parameter3>::__WNCallbackBase3() :
            mCount(0) {
        }

        template<typename Return, typename Parameter1, typename Parameter2, typename Parameter3>
        WN_VOID __WNCallbackBase3<Return, Parameter1, Parameter2, Parameter3>::AddRef() {
            WNAtomicIncrement(&mCount);
        }

        template<typename Return, typename Parameter1, typename Parameter2, typename Parameter3>
        WN_ATOM_T __WNCallbackBase3<Return, Parameter1, Parameter2, Parameter3>::RemoveRef() {
            return(WNAtomicDecrement(&mCount));
        }
    }
    
    template <typename Return>
    WNCallback0<Return>::WNCallback0() {
        mBaseCallback = WN_NULL;
    }

    template <typename Return>
    WNCallback0<Return>::WNCallback0(const WNCallback0<Return>& _callback) {
        mBaseCallback = _callback.mBaseCallback;

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template <typename Return>
    WNCallback0<Return>::WNCallback0(Return (*_function)()) {
        mBaseCallback = new __WNInternal::__WNCallbackClasslessInternal0<Return>(_function);

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template <typename Return>
    template <typename Type>
    WNCallback0<Return>::WNCallback0(Type* _this, Return (Type::*_function)()) {
        mBaseCallback = new __WNInternal::__WNCallbackInternal0<Type, Return>(_this, _function);

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template <typename Return>
    WNCallback0<Return>::~WNCallback0() {
        if (mBaseCallback) {
            if (mBaseCallback->RemoveRef() == 0) {
                WN_DELETE(mBaseCallback);
            }
        }
    }

    template<typename Return>
    WNCallback0<Return>& WNCallback0<Return>::operator = (const WNCallback0<Return>& _callback) {
        if (mBaseCallback) {
            if (mBaseCallback->RemoveRef() == 0) {
                WN_DELETE(mBaseCallback);
            }
        }

        mBaseCallback = _callback.mBaseCallback;

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }

        return(*this);
    }

    template<typename Return>
    Return WNCallback0<Return>::Execute() const {
        WN_DEBUG_ASSERT(mBaseCallback != WN_NULL);

        return(mBaseCallback->Execute());
    }

    template <typename Return, typename Parameter>
    WNCallback1<Return, Parameter>::WNCallback1() {
        mBaseCallback = WN_NULL;
    }

    template <typename Return, typename Parameter>
    WNCallback1<Return, Parameter>::WNCallback1(const WNCallback1<Return, Parameter>& _callback) {
        mBaseCallback = _callback.mBaseCallback;

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template <typename Return, typename Parameter>
    WNCallback1<Return, Parameter>::WNCallback1(Return (*_function)(Parameter)) {
        mBaseCallback = new __WNInternal::__WNCallbackClasslessInternal1<Return, Parameter>(_function);

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template <typename Return, typename Parameter>
    template <typename Type>
    WNCallback1<Return, Parameter>::WNCallback1(Type* _this, Return (Type::*_function)(Parameter)) {
        mBaseCallback = new __WNInternal::__WNCallbackInternal1<Type, Return, Parameter>(_this, _function);

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template <typename Return, typename Parameter>
    WNCallback1<Return, Parameter>::~WNCallback1() {
        if (mBaseCallback) {
            if (mBaseCallback->RemoveRef() == 0) {
                WN_DELETE(mBaseCallback);
            }
        }
    }

    template<typename Return, typename Parameter>
    WNCallback1<Return, Parameter>& WNCallback1<Return, Parameter>::operator = (const WNCallback1<Return, Parameter>& _callback) {
        if (mBaseCallback) {
            if (mBaseCallback->RemoveRef() == 0) {
                WN_DELETE(mBaseCallback);
            }
        }

        mBaseCallback = _callback.mBaseCallback;

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }

        return(*this);
    }

    template<typename Return, typename Parameter>
    Return WNCallback1<Return, Parameter>::Execute(const Parameter& _parameter) const {
        WN_DEBUG_ASSERT(mBaseCallback != WN_NULL);

        return(mBaseCallback->Execute(_parameter));
    }
    
    template<typename Return, typename Parameter1, typename Parameter2>
    WNCallback2<Return, Parameter1, Parameter2>::WNCallback2() {
        mBaseCallback = WN_NULL;
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    WNCallback2<Return, Parameter1, Parameter2>::WNCallback2(const WNCallback2<Return, Parameter1, Parameter2>& _callback) {
        mBaseCallback = _callback.mBaseCallback;

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    WNCallback2<Return, Parameter1, Parameter2>::WNCallback2(Return (*_function)(Parameter1, Parameter2)) {
        mBaseCallback = new __WNInternal::__WNCallbackClasslessInternal2<Return, Parameter1, Parameter2>(_function);

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    template<typename Type>
    WNCallback2<Return, Parameter1, Parameter2>::WNCallback2(Type* _this, Return (Type::*_function)(Parameter1, Parameter2)) {
        mBaseCallback =  new __WNInternal::__WNCallbackInternal2<Type, Return, Parameter1, Parameter2>(_this, _function);
        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    WNCallback2<Return, Parameter1, Parameter2>::~WNCallback2() {
        if (mBaseCallback) {
            if (mBaseCallback->RemoveRef() == 0) {
                WN_DELETE(mBaseCallback);
            }
        }
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    WNCallback2<Return, Parameter1, Parameter2>& WNCallback2<Return, Parameter1, Parameter2>::operator = (const WNCallback2<Return, Parameter1, Parameter2>& _callback) {
        if (mBaseCallback) {
            if (mBaseCallback->RemoveRef() == 0) {
                WN_DELETE(mBaseCallback);
            }
        }

        mBaseCallback = _callback.mBaseCallback;

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }

        return(*this);
    }

    template<typename Return, typename Parameter1, typename Parameter2>
    Return WNCallback2<Return, Parameter1, Parameter2>::Execute(const Parameter1& _parameter1, const Parameter2& _parameter2) const {
        WN_DEBUG_ASSERT(mBaseCallback != WN_NULL);

        return(mBaseCallback->Execute(_parameter1, _parameter2));
    }

    template<typename Return, typename Parameter1, typename Parameter2, typename Parameter3>
    WNCallback3<Return, Parameter1, Parameter2, Parameter3>::WNCallback3() {
        mBaseCallback = WN_NULL;
    }

    template<typename Return, typename Parameter1, typename Parameter2, typename Parameter3>
    WNCallback3<Return, Parameter1, Parameter2, Parameter3>::WNCallback3(const WNCallback3<Return, Parameter1, Parameter2, Parameter3>& _callback) {
        mBaseCallback = _callback.mBaseCallback;

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template<typename Return, typename Parameter1, typename Parameter2, typename Parameter3>
    WNCallback3<Return, Parameter1, Parameter2, Parameter3>::WNCallback3(Return (*_function)(Parameter1, Parameter2, Parameter3)) {
        mBaseCallback = new __WNInternal::__WNCallbackClasslessInternal3<Return, Parameter1, Parameter2, Parameter3>(_function);

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template<typename Return, typename Parameter1, typename Parameter2, typename Parameter3>
    template<typename Type>
    WNCallback3<Return, Parameter1, Parameter2, Parameter3>::WNCallback3(Type* _this, Return (Type::*_function)(Parameter1, Parameter2, Parameter3)) {
        mBaseCallback =  new __WNInternal::__WNCallbackInternal3<Type, Return, Parameter1, Parameter2, Parameter3>(_this, _function);
        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }
    }

    template<typename Return, typename Parameter1, typename Parameter2, typename Parameter3>
    WNCallback3<Return, Parameter1, Parameter2, Parameter3>::~WNCallback3() {
        if (mBaseCallback) {
            if (mBaseCallback->RemoveRef() == 0) {
                WN_DELETE(mBaseCallback);
            }
        }
    }

    template<typename Return, typename Parameter1, typename Parameter2, typename Parameter3>
    WNCallback3<Return, Parameter1, Parameter2, Parameter3>& WNCallback3<Return, Parameter1, Parameter2, Parameter3>::operator = (const WNCallback3<Return, Parameter1, Parameter2, Parameter3>& _callback) {
        if (mBaseCallback) {
            if (mBaseCallback->RemoveRef() == 0) {
                WN_DELETE(mBaseCallback);
            }
        }

        mBaseCallback = _callback.mBaseCallback;

        if (mBaseCallback) {
            mBaseCallback->AddRef();
        }

        return(*this);
    }

    template<typename Return, typename Parameter1, typename Parameter2, typename Parameter3>
    Return WNCallback3<Return, Parameter1, Parameter2, Parameter3>::Execute(const Parameter1& _parameter1, const Parameter2& _parameter2, const Parameter3& _parameter3) const {
        WN_DEBUG_ASSERT(mBaseCallback != WN_NULL);

        return(mBaseCallback->Execute(_parameter1, _parameter2, _parameter3));
    }
}

#endif // __WN_CONTAINERS_CALLBACK_INL__
